﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace System.Windows.Controls
{
    /// <summary>
    /// Implements a <see cref="System.Windows.Controls.ItemsControl"/> derived base class which provides selection support.
    /// </summary>
    public class SelectionItemsControl : System.Windows.Controls.ItemsControl
    {
        /// <summary>
        /// Fires when an item has been selected.
        /// </summary>
        public event EventHandler<ItemControlEventArgs> ItemSelected = null;

        /// <summary>
        /// Initializes an instance of the <see cref="SelectionItemsControl"/>.
        /// </summary>
        public SelectionItemsControl()
        {
            this.IsEnabledChanged += new DependencyPropertyChangedEventHandler(OnIsEnabledChanged);
            this.SelectedIndex = -1;
        }

        private ObservableCollection<ItemControl>   _ItemControls = new ObservableCollection<ItemControl>();

        /// <summary>
        /// Identifies the <see cref="SelectedItem"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty   SelectedItemProperty = RegisterDependencyProperty(typeof(SelectionItemsControl), "SelectedItem", typeof(ItemControl));

        /// <summary>
        /// Identifies the <see cref="SelectedIndex"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty   SelectedIndexProperty = RegisterDependencyProperty(typeof(SelectionItemsControl), "SelectedIndex", typeof(int));
        
        #region Dependency Property Helpers

        /// <summary>
        /// A static helper method for registering a <see cref="System.Windows.DependencyProperty"/>.
        /// </summary>
        /// <param name="controlType">The <see cref="System.Type"/> for the control to which the property belongs</param>
        /// <param name="name">The name of the property</param>
        /// <param name="propertyType">The <see cref="System.Type"/> for the value of the property</param>
        /// <returns>A <see cref="System.Windows.DependencyProperty"/> instance</returns>
        protected static DependencyProperty RegisterDependencyProperty(Type controlType, string name, Type propertyType)
        {
            DependencyProperty prop = DependencyProperty.Register(name, propertyType, controlType, new PropertyMetadata(new PropertyChangedCallback(OnHandleDependencyPropertyChanged)));
            return prop;
        }

        /// <summary>
        /// A static method for handling <see cref="System.Windows.DependencyProperty"/> changes.
        /// </summary>
        /// <param name="obj">The <see cref="System.Windows.DependencyObject"/> against which the property change should be applied</param>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> event argument</param>
        protected static void OnHandleDependencyPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (obj is SelectionItemsControl)
            {
                ((SelectionItemsControl)obj).OnDependencyPropertyChanged(e);
            }
        }

        /// <summary>
        /// A virtual handler for processing <see cref="System.Windows.DependencyProperty"/> changes in derived controls.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> event argument</param>
        protected virtual void OnDependencyPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.Property == SelectedItemProperty)
            {
                this.SelectedItem = e.NewValue as ItemControl;
            }
            else if (e.Property == SelectedIndexProperty)
            {
                this.SelectedIndex = (int)e.NewValue;
            }
        }

        #endregion

        /// <summary>
        /// Gets/Sets a value which determines the currently selected item
        /// </summary>
        public ItemControl SelectedItem
        {
            get { return GetValue(SelectedItemProperty) as ItemControl; }
            set 
            { 
                ItemControl currentItem = this.SelectedItem;
                if (currentItem != null && currentItem != value) currentItem.IsSelected = false;
                SetValue(SelectedItemProperty, value);
                if (value != null)
                {
                    value.IsSelected = true;
                    if (this.ItemSelected != null) this.ItemSelected(this, new ItemControlEventArgs(value));
                }
            }
        }

        /// <summary>
        /// Gets/Sets a value which determines the index of the currently selected item
        /// </summary>
        public int SelectedIndex
        {
            get { return (int)GetValue(SelectedIndexProperty); }
            set { 
                if (value >= 0 && value < _ItemControls.Count)
                {
                    SetValue(SelectedIndexProperty, value);
                    this.SelectedItem = _ItemControls[value];
                }
            }
        }

        #region Template Helpers

        /// <summary>
        /// Provides a templated method for finding a template child
        /// </summary>
        /// <typeparam name="T">The expected type of the child element</typeparam>
        /// <param name="name">The name of the child element to find</param>
        /// <returns>An instance of T or null if the item has not been found</returns>
        protected T VerifyTemplateChild<T>(string name) where T:class
        {
            T element = GetTemplateChild(name) as T;
            if (element == null && DesignerProperties.GetIsInDesignMode(this))
            {
                throw new InvalidOperationException(String.Format("Template for {0} missing required element, Name={1}, Type={2}", this.GetType().Name, name, typeof(T)));
            }

            return element;
        }

        /// <summary>
        /// Provides a wrapper method for switching between Visual States.
        /// </summary>
        /// <param name="useTransitions">A boolean value indicating whether transitions should be used</param>
        /// <param name="stateName">The name of the state to assume</param>
        /// <returns>A boolean value indicating whether the state was successfully found</returns>
        protected bool GoToState(bool useTransitions, string stateName)
        {
            return VisualStateManager.GoToState(this, stateName, useTransitions);
        }

        #endregion

        internal void ChangeSelection(System.Windows.Controls.Control control, bool selected)
        {
            ItemControl item = control as ItemControl;
            if (item != null)
            {
                this.SelectedItem = (selected ? item : null);
            }
        }

        /// <summary>
        /// (Override) Clears conditions for an existing <see cref="System.Windows.DependencyObject"/> which is being removed from the control.
        /// </summary>
        /// <param name="element">The element to remove</param>
        /// <param name="item">The data item associated with the element</param>
        protected override void ClearContainerForItemOverride(DependencyObject element, object item)
        {
            ItemControl control = element as ItemControl;
            if (control != null)
            {
                if (_ItemControls.Contains(control)) _ItemControls.Remove(control);
                control.ParentItemsControl = null;
            }

            base.ClearContainerForItemOverride(element, item);
        }

        /// <summary>
        /// (Override) Changes the behavior of the <see cref="System.Windows.Controls.ItemsControl"/> collection handler.
        /// </summary>
        /// <param name="e">The collection notification event arguments</param>
        protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (object child in e.NewItems)
                {
                    if (child is ItemControl)
                    {
                        ItemControl item = (ItemControl)child;
                        item.ParentItemsControl = this;
                    }
                }
            }

            base.OnItemsChanged(e);
        }

        /// <summary>
        /// (Override) Prepares an element for insertion into the control
        /// </summary>
        /// <param name="element">The visual element being added to the control</param>
        /// <param name="item">The data item associated with the visual element</param>
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);

            ItemControl control = element as ItemControl;
            if (control != null)
            {
                if (!_ItemControls.Contains(control)) _ItemControls.Add(control);
                control.ParentItemsControl = this;

                if (this.SelectedIndex == this.Items.IndexOf(item))
                {
                    this.SelectedItem = control;
                }
            }
        }

        /// <summary>
        /// Provides a virtual handler which fires when the control is enabled or disabled.
        /// </summary>
        /// <param name="sender">The sender of the event</param>
        /// <param name="e">The event args for the property change</param>
        protected virtual void OnIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            
        }
    }
}
